﻿namespace NJQuery {
	using System;
	using System.ComponentModel;

	/// <summary>
	/// Defines a predefined or custom <see cref="Dialog"/> widget animation.
	/// </summary>
	[TypeConverter(typeof(DialogAnimationConverter))]
	public struct DialogAnimation : IEquatable<DialogAnimation>, IComplexProperty {


		#region Static Readonly Fields: Standard values

		/// <summary>
		/// No animation will be used.
		/// </summary>
		public static readonly DialogAnimation Immediate = new DialogAnimation();

		/// <summary>
		/// Instructs jQuery UI to animate using the duration associated with the predefined "normal" animaiton speed.
		/// </summary>
		public static readonly DialogAnimation Normal = new DialogAnimation(NJQuery.Duration.Normal);

		/// <summary>
		/// Instructs jQuery UI to animate using the duration associated with the predefined "fast" animaiton speed.
		/// </summary>
		public static readonly DialogAnimation Fast = new DialogAnimation(NJQuery.Duration.Fast);

		/// <summary>
		/// Instructs jQuery UI to animate using the duration associated with the predefined "slow" animaiton speed.
		/// </summary>
		public static readonly DialogAnimation Slow = new DialogAnimation(NJQuery.Duration.Slow);

		#endregion


		#region Static Methods

		/// <summary>
		/// Converts a string representation to its <see cref="DialogAnimation"/> equivalent.
		/// </summary>
		/// <param name="text">A string containing the value to convert.</param>
		/// <returns>The parsed value.</returns>
		public static DialogAnimation Parse(string text) {

			if(string.IsNullOrEmpty(text)) return Immediate;

			Duration duration;
			if(NJQuery.Duration.TryParse(text, out duration)) return new DialogAnimation(duration);

			return new DialogAnimation(text);
		}

		#endregion


		#region Fields

		private Duration duration;
		private string controlId;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new <see cref="DialogAnimation"/> value using a duration.
		/// </summary>
		/// <param name="duration">The speed at which the dialog shows or hides.</param>
		public DialogAnimation(Duration duration) {
			this.duration = duration;
			controlId = null;
		}

		/// <summary>
		/// Initializes a new <see cref="DialogAnimation"/> value using a jQuery UI effect definition.
		/// Currently the options and speed will be ignored.  This may be available in future versions of jQuery UI.
		/// </summary>
		/// <param name="effect">The effect definition to use.</param>
		/// <exception cref="ArgumentNullException">The animation definition value cannot be null.</exception>
		/// <exception cref="ArgumentException">The animation definition must have a name specified.</exception>
		public DialogAnimation(IEffect effect) {
			if(effect == null) throw new ArgumentNullException("effect");
			if(string.IsNullOrEmpty(effect.ControlId)) throw new ArgumentException("Effect object must have control ID specified.", "effect");

			duration = NJQuery.Duration.Immediate;
			controlId = effect.ControlId;
		}

		/// <summary>
		/// Initializes a new <see cref="DatePickerAnimation"/> value using a jQuery UI effect server control definition.
		/// Currently the options and speed will be ignored.  This may be available in future versions of jQuery UI.
		/// </summary>
		/// <param name="controlId">The control ID of the effect definition to use.</param>
		/// <exception cref="ArgumentNullException">The control ID cannot be null.</exception>
		/// <exception cref="ArgumentException">The control ID is required.</exception>
		public DialogAnimation(string controlId) {
			if(controlId == null) throw new ArgumentNullException("controlId");
			if(controlId.Length == 0) throw new ArgumentException("Value is reqired.", "controlId");

			duration = NJQuery.Duration.Immediate;
			this.controlId = controlId;
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets whether the Dialog widget should show without any animation.
		/// </summary>
		public bool IsNone {
			get { return duration == NJQuery.Duration.Immediate && controlId == null; }
		}

		/// <summary>
		/// Gets the <see cref="Duration"/> to use for the default dialog show behavior, if applicable.
		/// </summary>
		public Duration? Duration {
			get { return controlId == null ? duration : (Duration?)null; }
		}

		/// <summary>
		/// Gets the ID of a server effect control to use, if applicable.
		/// </summary>
		public string ControlId {
			get { return controlId; }
		}

		#endregion


		#region Methods: Object management

		/// <summary>
		/// Determines whether or not the specified object is equal to the current <see cref="DialogAnimation"/> value.
		/// </summary>
		/// <param name="obj">The <see cref="Object"/> to compare.</param>
		/// <returns>Whether or not the provided object is equal.</returns>
		public override bool Equals(object obj) {
			if(obj is DialogAnimation) return this == (DialogAnimation)obj;
			return false;
		}

		/// <summary>
		/// Determines whether or not the specified DialogAnimation is equal to the current <see cref="DialogAnimation"/> value.
		/// </summary>
		/// <param name="other">The <see cref="DialogAnimation"/> to compare.</param>
		/// <returns>Whether or not the provided <see cref="DialogAnimation"/> value is equal.</returns>
		public bool Equals(DialogAnimation other) {
			return this == other;
		}

		/// <summary>
		/// Serves as a hash function for <see cref="DialogAnimation"/>.
		/// </summary>
		/// <returns>The hash value associated with the contained data.</returns>
		public override int GetHashCode() {
			//TODO: Have TheMuuj do it correctly.
			return duration.GetHashCode() % controlId.GetHashCode();
		}

		/// <summary>
		/// Returns a <see cref="String"/> that represents the current <see cref="DialogAnimation"/> value.
		/// </summary>
		/// <returns>The string representation.</returns>
		public override string ToString() {
			return controlId ?? duration.ToString();
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <returns>The client script value.</returns>
		public string ToClientSource() {
			if(controlId == null) return duration.ToClientSource();
			return ScriptBuilder.NullLiteral;
		}

		#endregion


		#region Operators

		/// <summary>
		/// Compares the equality of two <see cref="DialogAnimation"/> values.
		/// </summary>
		/// <param name="value0">The first value.</param>
		/// <param name="value1">The second value.</param>
		/// <returns>Whether or not the two values are equal.</returns>
		public static bool operator ==(DialogAnimation value0, DialogAnimation value1) {
			return value0.duration == value1.duration && value0.controlId == value1.controlId;
		}

		/// <summary>
		/// Compares the inequality of two <see cref="DialogAnimation"/> values.
		/// </summary>
		/// <param name="value0">The first value.</param>
		/// <param name="value1">The second value.</param>
		/// <returns>Whether or not the two values are distinct.</returns>
		public static bool operator !=(DialogAnimation value0, DialogAnimation value1) {
			return value0.duration != value1.duration || value0.controlId != value1.controlId;
		}

		#endregion


	}

}